﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Spire.Doc;
using Spire.Doc.Documents;
using Spire.Doc.Fields;
using Spire.Presentation;
using Spire.Xls;
using Spire.Xls.Core;

namespace engine
{
    #region 引擎
    public class Engine
    {
        private IParser _parser;
        private IRender _render;
        private ITemplateFunc _matchTemplateFunc;
        private ITemplateFunc _renderTemplateFunc;
        public EngineRuntime EngineRuntime { get; }

        public Engine(EngineRuntime engineRuntime)
        {
            EngineRuntime = engineRuntime;
            _parser = new Parser();
            _render = new Render(EngineRuntime);
            _matchTemplateFunc = new TemplateFunc(FuncType.Match);
            _renderTemplateFunc = new TemplateFunc(FuncType.Render);
        }

        public Engine(IParser parser, IRender render, ITemplateFunc matchTemplateFunc, ITemplateFunc renderTemplateFunc)
        {
            if(parser != null) _parser = parser;
            if(render != null) _render = render;
            if(_matchTemplateFunc != null) _matchTemplateFunc = matchTemplateFunc;
            if(_renderTemplateFunc != null) _renderTemplateFunc = renderTemplateFunc;
        }

        public void SetRenderTemplateFunc(ITemplateFunc renderTemplateFunc)
        {
            _renderTemplateFunc = renderTemplateFunc;
        }

        private NodeBase ParseTemplate(string templateName, GlobalConfig globalConfig)
        {
            EngineRuntime.Hook.BeforeParse();
            NodeBase nodeBase = _parser.ParseTemplate(templateName, globalConfig);
            EngineRuntime.Hook.AfterParse();
            return nodeBase;
        }

        public Dictionary<string, DataItem> MatchFile(string templateName, string configName, string fileName)
        {
            GlobalConfig globalConfig = new GlobalConfig();
            globalConfig.RootFuncNode.SetTemplateFunc(_matchTemplateFunc);
            globalConfig.TemplateConfig = new TemplateConfig(configName, globalConfig.RootFuncNode);

            List<Unit> units = ParseTemplate(fileName, globalConfig).RenderValue(null);

            NodeBase nodeBase = ParseTemplate(templateName, globalConfig);

            EngineRuntime.Hook.BeforeMatch();
            nodeBase.MatchData(units, globalConfig.RootFuncNode, null);
            EngineRuntime.Hook.AfterMatch();

            var datas = globalConfig.RootFuncNode.GetDatasWithChildren(globalConfig.TemplateConfig);
            EngineRuntime.Hook.AfterData();
            return datas;
        }

        public void RenderTemplate(string templateName, string configName, Dictionary<string, DataItem> data, string outputName)
        {
            GlobalConfig globalConfig = new GlobalConfig();
            globalConfig.RootFuncNode.SetTemplateFunc(_renderTemplateFunc);
            globalConfig.TemplateConfig = new TemplateConfig(configName, globalConfig.RootFuncNode);
            globalConfig.TemplateConfig.SetDatas(data);

            //NodeBase nodeBase = ParseTemplate(templateName, globalConfig);
            //List<Unit> units = nodeBase.RenderValue(null);

            //_render.RenderToFile(units, outputName);

            NodeBase nodeBase = ParseTemplate(templateName, globalConfig);

            FileInfo fileInfo = new FileInfo(templateName);
            EngineRuntime.Hook.BeforeRender();
            if (fileInfo.Extension.Contains("doc"))
            {
                _render.RenderToFile(nodeBase as DocumentNode, outputName);
            }
            else if (fileInfo.Extension.Contains("xls"))
            {
                _render.RenderToFile(nodeBase as WorkbookNode, outputName);
            }
            else if (fileInfo.Extension.Contains("ppt"))
            {
                _render.RenderToFile(nodeBase as PresentationNode, outputName);
            }
            else
            {
                throw new Exception("文件格式不合法");
            }
            EngineRuntime.Hook.AfterRender();
        }
    }
    #endregion

    #region 解析器和渲染器
    public class Parser: IParser
    {
        public NodeBase ParseTemplate(string templateName, GlobalConfig globalConfig)
        {
            FileInfo fileInfo = new FileInfo(templateName);

            if (fileInfo.Extension.Contains("doc"))
            {
                Document document = new Document(templateName);
                NodeBase nodeBase = ParseDocument(null, document, globalConfig);
                return nodeBase;
            }
            else if (fileInfo.Extension.Contains("xls"))
            {
                Workbook workbook = new Workbook();
                workbook.LoadFromFile(templateName);
                NodeBase nodeBase = ParseWorkbook(null, workbook, globalConfig);
                return nodeBase;
            }
            else if (fileInfo.Extension.Contains("ppt"))
            {
                Presentation presentation = new Presentation(templateName, Spire.Presentation.FileFormat.PPT);
                NodeBase nodeBase = ParsePresentation(null, presentation, null, globalConfig);
                return nodeBase;
            }
            else
            {
                throw new Exception("文件格式不合法");
            }
        }

        public NodeBase ParseDocument(NodeBase parent, DocumentObject doc, GlobalConfig config)
        {
            NodeBase result = null;

            switch (doc.DocumentObjectType)
            {
                case DocumentObjectType.Document:
                    {
                        DocumentNode tempNode = new DocumentNode(doc);
                        Document document = doc as Document;

                        if (parent != null)
                        {
                            parent.AddChild(tempNode);
                        }

                        foreach (DocumentObject child in document.Sections)
                        {
                            var node = ParseDocument(tempNode, child, config);
                        }

                        foreach (DocumentObject child in document.TextBoxes)
                        {
                            var node = ParseDocument(null, child, config);
                            tempNode.TextBoxes.Add(node as TextBoxNode);
                        }

                        result = tempNode;
                        break;
                    }
                case DocumentObjectType.TextBox:
                    {
                        TextBoxNode tempNode = new TextBoxNode(doc);
                        TextBox textBox = doc as TextBox;

                        if (parent != null)
                        {
                            parent.AddChild(tempNode);
                        }

                        foreach (DocumentObject child in textBox.ChildObjects)
                        {
                            var node = ParseDocument(tempNode, child, config);
                        }

                        result = tempNode;
                        break;
                    }
                case DocumentObjectType.Section:
                    {
                        SectionNode tempNode = new SectionNode(doc);
                        Section section = doc as Section;

                        if (parent != null)
                        {
                            parent.AddChild(tempNode);
                        }

                        foreach (DocumentObject child in section.Body.ChildObjects)
                        {
                            var node = ParseDocument(tempNode, child, config);
                        }

                        result = tempNode;
                        break;
                    }
                case DocumentObjectType.Table:
                    {
                        TableNode tempNode = new TableNode(doc);
                        Table table = doc as Table;

                        if (parent != null)
                        {
                            parent.AddChild(tempNode);
                        }

                        foreach (DocumentObject child in table.Rows)
                        {
                            var node = ParseDocument(tempNode, child, config);
                        }

                        result = tempNode;
                        break;
                    }
                case DocumentObjectType.TableRow:
                    {
                        RowNode tempNode = new RowNode(doc);
                        Spire.Doc.TableRow tableRow = doc as Spire.Doc.TableRow;

                        if (parent != null)
                        {
                            parent.AddChild(tempNode);
                        }

                        foreach (DocumentObject child in tableRow.Cells)
                        {
                            var node = ParseDocument(tempNode, child, config);
                        }

                        result = tempNode;
                        break;
                    }
                case DocumentObjectType.TableCell:
                    {
                        CellNode tempNode = new CellNode(doc);
                        TableCell tableCell = doc as TableCell;

                        if (parent != null)
                        {
                            parent.AddChild(tempNode);
                        }

                        foreach (DocumentObject child in tableCell.ChildObjects)
                        {
                            var node = ParseDocument(tempNode, child, config);
                        }

                        result = tempNode;
                        break;
                    }
                case DocumentObjectType.Body:
                    {
                        BodyNode tempNode = new BodyNode(doc);

                        if (parent != null)
                        {
                            parent.AddChild(tempNode);
                        }

                        foreach (DocumentObject child in doc.ChildObjects)
                        {
                            var node = ParseDocument(tempNode, child, config);
                        }

                        result = tempNode;
                        break;
                    }
                case DocumentObjectType.Paragraph:
                    {
                        ParagraphNode tempNode = new ParagraphNode(doc);
                        Paragraph paragraph = doc as Paragraph;

                        if (parent != null)
                        {
                            parent.AddChild(tempNode);
                        }

                        foreach (DocumentObject child in paragraph.ChildObjects)
                        {
                            if(child is Spire.Doc.Fields.TextRange || child is DocPicture)
                            {
                                var node = ParseDocument(tempNode, child, config);
                            }
                        }

                        // 从段落层面开始检测是否有变量存在
                        HandleMarker(tempNode, config);

                        result = tempNode;
                        break;
                    }
                case DocumentObjectType.TextRange:
                    {
                        Spire.Doc.Fields.TextRange textRange = doc as Spire.Doc.Fields.TextRange;
                        TextRangeNode tempNode = new TextRangeNode(textRange.Text, doc);

                        if (parent != null)
                        {
                            parent.AddChild(tempNode);
                        }

                        result = tempNode;
                        break;
                    }
                case DocumentObjectType.Picture:
                    {
                        PictureNode tempNode = new PictureNode(doc);
                        DocPicture docPicture = doc as DocPicture;

                        if (parent != null)
                        {
                            parent.AddChild(tempNode);
                        }

                        tempNode.Image = docPicture.Image;

                        result = tempNode;
                        break;
                    }
            }
            
            return result;
        }

        public NodeBase ParseWorkbook(NodeBase parent, object obj, GlobalConfig config)
        {
            NodeBase result = null;

            if(obj is Workbook)
            {
                WorkbookNode tempNode = new WorkbookNode(obj);
                Workbook workbook = obj as Workbook;

                if(parent != null)
                {
                    parent.AddChild(tempNode);
                }

                foreach(var child in workbook.Worksheets)
                {
                    ParseWorkbook(tempNode, child, config);
                }

                result = tempNode;
            }
            else if(obj is Worksheet)
            {
                WorksheetNode tempNode = new WorksheetNode(obj);
                Worksheet worksheet = obj as Worksheet;

                if (parent != null)
                {
                    parent.AddChild(tempNode);
                }
                
                foreach(var child in worksheet.Rows)
                {
                    ParseWorkbook(tempNode.Table, child, config);
                }
                for(int i = 0; i < worksheet.TextBoxes.Count; i++)
                {
                    var node = ParseWorkbook(tempNode, worksheet.TextBoxes[i], config);
                    tempNode.TextBoxes.Add(node as TextBoxNode);
                }
                for (int i = 0; i < worksheet.Pictures.Count; i++)
                {
                    var node = ParseWorkbook(tempNode, worksheet.Pictures[i], config);
                    tempNode.Pictures.Add(node as PictureNode);
                }

                result = tempNode;
            }
            else if(obj is ITextBox && parent is WorksheetNode)
            {
                TextBoxNode tempNode = new TextBoxNode(obj);
                ITextBox textBox = obj as ITextBox;

                if(parent != null)
                {
                    parent.AddChild(tempNode);
                }

                if (!string.IsNullOrWhiteSpace(textBox.Text))
                {
                    ParseWorkbook(tempNode, textBox, config);
                }

                result = tempNode;
            }
            else if (obj is CellRange && parent is TableNode)
            {
                RowNode tempNode = new RowNode(obj);
                CellRange row = obj as CellRange;

                if (parent != null)
                {
                    parent.AddChild(tempNode);
                }
                
                foreach (var child in row.Cells)
                {
                    ParseWorkbook(tempNode, child, config);
                }

                result = tempNode;
            }
            else if (obj is CellRange && parent is RowNode)
            {
                CellNode tempNode = new CellNode(obj);
                CellRange cell = obj as CellRange;

                if (parent != null)
                {
                    parent.AddChild(tempNode);
                }

                if (!string.IsNullOrWhiteSpace(cell.Text))
                {
                    ParseWorkbook(tempNode, cell, config);
                }

                result = tempNode;
            }
            else if (obj is IPictureShape)
            {
                PictureNode tempNode = new PictureNode(obj);
                IPictureShape picture = obj as IPictureShape;

                if (parent != null)
                {
                    parent.AddChild(tempNode);
                }

                tempNode.Image = picture.Picture;
                object temp = MarkerParser.ParseText(picture.AlternativeText, config);
                if(temp is FuncNode)
                {
                    tempNode.FuncNode = temp as FuncNode;
                }

                result = tempNode;
            }
            else if (parent is TextBoxNode || parent is CellNode)
            {
                string text = obj is ITextBox ? (obj as ITextBox).Text : (obj as CellRange).Text;
                TextRangeNode textRangeNode = new TextRangeNode(text, obj);

                ParagraphNode paragraphNode = new ParagraphNode(obj);
                paragraphNode.AddChild(textRangeNode);

                if (parent != null)
                {
                    parent.AddChild(paragraphNode);
                }

                HandleMarker(paragraphNode, config);

                result = paragraphNode;
            }

            return result;
        }

        public NodeBase ParsePresentation(NodeBase parent, object obj, object objParent, GlobalConfig config)
        {
            NodeBase result = null;

            if (obj is Presentation)
            {
                PresentationNode tempNode = new PresentationNode(obj);
                Presentation presentation = obj as Presentation;

                if (parent != null)
                {
                    parent.AddChild(tempNode);
                }

                foreach (var child in presentation.Slides)
                {
                    ParsePresentation(tempNode, child, obj, config);
                }

                result = tempNode;
            }
            else if(obj is ISlide)
            {
                SlideNode tempNode = new SlideNode(obj, objParent);
                ISlide slide = obj as ISlide;

                if (parent != null)
                {
                    parent.AddChild(tempNode);
                }

                foreach (var child in slide.Shapes)
                {
                    ParsePresentation(tempNode, child, obj, config);
                }

                result = tempNode;
            }
            else if(obj is IAutoShape)
            {
                TextBoxNode tempNode = new TextBoxNode(obj, objParent);
                IAutoShape textbox = obj as IAutoShape;

                if (parent != null)
                {
                    parent.AddChild(tempNode);
                }

                foreach (var child in textbox.TextFrame.Paragraphs)
                {
                    ParsePresentation(tempNode, child, textbox, config);
                }

                result = tempNode;
            }
            else if(obj is ITable)
            {
                TableNode tempNode = new TableNode(obj, objParent);
                ITable table = obj as ITable;

                if (parent != null)
                {
                    parent.AddChild(tempNode);
                }

                foreach (var child in table.TableRows)
                {
                    ParsePresentation(tempNode, child, obj, config);
                }

                result = tempNode;
            }
            else if (obj is Spire.Presentation.TableRow)
            {
                RowNode tempNode = new RowNode(obj, objParent);
                Spire.Presentation.TableRow row = obj as Spire.Presentation.TableRow;

                if (parent != null)
                {
                    parent.AddChild(tempNode);
                }

                foreach (var child in row)
                {
                    ParsePresentation(tempNode, child, obj, config);
                }

                result = tempNode;
            }
            else if (obj is Spire.Presentation.Cell)
            {
                CellNode tempNode = new CellNode(obj, objParent);
                Spire.Presentation.Cell cell = obj as Spire.Presentation.Cell;

                if (parent != null)
                {
                    parent.AddChild(tempNode);
                }

                foreach (var child in cell.TextFrame.Paragraphs)
                {
                    ParsePresentation(tempNode, child, cell, config);
                }

                result = tempNode;
            }
            else if (obj is Spire.Presentation.TextParagraph)
            {
                ParagraphNode tempNode = new ParagraphNode(obj, objParent);
                Spire.Presentation.TextParagraph paragraph = obj as Spire.Presentation.TextParagraph;

                if (parent != null)
                {
                    parent.AddChild(tempNode);
                }

                foreach (var child in paragraph.TextRanges)
                {
                    ParsePresentation(tempNode, child, obj, config);
                }

                HandleMarker(tempNode, config);

                result = tempNode;
            }
            else if (obj is Spire.Presentation.TextRange)
            {
                Spire.Presentation.TextRange textRange = obj as Spire.Presentation.TextRange;
                TextRangeNode tempNode = new TextRangeNode(textRange.Text, obj, objParent);

                if (parent != null)
                {
                    parent.AddChild(tempNode);
                }

                result = tempNode;
            }
            else if (obj is Spire.Presentation.SlidePicture)
            {
                PictureNode tempNode = new PictureNode(obj, objParent);
                Spire.Presentation.SlidePicture picture = obj as Spire.Presentation.SlidePicture;

                if (parent != null)
                {
                    parent.AddChild(tempNode);
                }

                tempNode.Image = picture.PictureFill.Picture.EmbedImage.Image;
                object temp = MarkerParser.ParseText(picture.AlternativeText, config);
                if (temp is FuncNode)
                {
                    tempNode.FuncNode = temp as FuncNode;
                }

                result = tempNode;
            }

            return result;
        }

        /// <summary>
        /// 以段落为单位，识别标记并处理
        /// </summary>
        /// <param name="paragraphNode"></param>
        void HandleMarker(ParagraphNode paragraphNode, GlobalConfig config)
        {
            MarkerParser markerParser = new MarkerParser(paragraphNode, config);
            markerParser.Parse();
        }
    }
    public interface IParser
    {
        NodeBase ParseTemplate(string templateName, GlobalConfig globalConfig);
    }
    
    public class Render: IRender
    {
        private EngineRuntime _rt;
        public Render(EngineRuntime rt)
        {
            _rt = rt;
        }

        #region Word
        public void RenderToFile(DocumentNode nodeBase, string outputName)
        {
            _rt.Hook.BeforeRender();
            RenderToWordSource(nodeBase);
            _rt.Hook.AfterRender();
            var doc = nodeBase.Source as Document;
            doc.SaveToFile(outputName);
        }

        /// <summary>
        /// 直接修改源
        /// </summary>
        /// <param name="nodeBase"></param>
        private void RenderToWordSource(NodeBase nodeBase)
        {
            if (nodeBase.JustSource)
            {
                return;
            }
            
            if(nodeBase.FuncNode != null || nodeBase is ParagraphNode)
            {
                // 先渲染所有的unit，然后将它们附加到source后面，并删掉source
                List<Unit> units = nodeBase.RenderValue(null);
                var doc = nodeBase.Source as DocumentObject;
                foreach(var unit in units)
                {
                    RenderToDocument(unit, doc, true);
                }
                doc.Owner.ChildObjects.Remove(doc);
            }
            else
            {
                foreach(var child in nodeBase.Children)
                {
                    RenderToWordSource(child);
                }
            }

            if(nodeBase is DocumentNode)
            {
                var documentNode = nodeBase as DocumentNode;
                foreach (var textbox in documentNode.TextBoxes)
                {
                    RenderToWordSource(textbox);
                }
            }
        }

        /// <summary>
        /// 如果源中要修改的内容比较复杂，需要整体修改，则使用该函数，如循环渲染多个表格行
        /// </summary>
        /// <param name="unit"></param>
        /// <param name="doc"></param>
        /// <param name="isAfter"></param>
        private void RenderToDocument(Unit unit, DocumentObject doc, bool isInsert = false)
        {
            if (unit is TextBoxUnit)
            {
                TextBoxUnit textBoxUnit = unit as TextBoxUnit;
                Document document = doc as Document;

                TextBox textBox = textBoxUnit.Source as TextBox;
                TextBox newTextBox = document.Sections[0].AddParagraph().AppendTextBox((float)textBox.Width, (float)textBox.Height);
                newTextBox.HorizontalPosition = textBox.HorizontalPosition;
                newTextBox.VerticalPosition = textBox.VerticalPosition;

                if (!unit.JustSource)
                {
                    //newTextBox.Body.ChildObjects.Clear();
                    foreach (var childUnit in textBoxUnit.Children)
                    {
                        RenderToDocument(childUnit, newTextBox.Body);
                    }
                }
            }
            else if (unit is SectionUnit)
            {
                SectionUnit sectionUnit = unit as SectionUnit;
                //Section section = new Section(doc.Document);

                Section newSection = (sectionUnit.Source as Section).Clone() as Section;
                if (!unit.JustSource)
                {
                    newSection.Body.ChildObjects.Clear();
                    foreach (var rowUnit in sectionUnit.Children)
                    {
                        RenderToDocument(rowUnit, newSection.Body);
                    }
                }

                if (isInsert)
                {
                    doc.Owner.ChildObjects.Insert(doc.Owner.ChildObjects.IndexOf(doc), newSection);
                }
                else
                {
                    doc.ChildObjects.Add(newSection);
                }
            }
            else if (unit is TableUnit)
            {
                TableUnit tableUnit = unit as TableUnit;
                //Table table = new Table(doc.Document);
                //table.DefaultColumnsNumber = tableUnit.ColumnCount;

                Table newTable = (tableUnit.Source as Table).Clone() as Table;
                if (!unit.JustSource)
                {
                    newTable.ChildObjects.Clear();
                    foreach (var rowUnit in tableUnit.Rows)
                    {
                        RenderToDocument(rowUnit, newTable);
                    }
                }

                if (isInsert)
                {
                    doc.Owner.ChildObjects.Insert(doc.Owner.ChildObjects.IndexOf(doc), newTable);
                }
                else
                {
                    doc.ChildObjects.Add(newTable);
                }
            }
            else if (unit is RowUnit)
            {
                RowUnit rowUnit = unit as RowUnit;
                Table table = doc as Table;
                //TableRow tableRow = new TableRow(doc.Document);

                Spire.Doc.TableRow newTableRow = (rowUnit.Source as Spire.Doc.TableRow).Clone() as Spire.Doc.TableRow;
                if (!unit.JustSource)
                {
                    newTableRow.ChildObjects.Clear();
                    foreach (var cellUnit in rowUnit.Cells)
                    {
                        RenderToDocument(cellUnit, newTableRow);
                    }
                }

                if (isInsert)
                {
                    doc.Owner.ChildObjects.Insert(doc.Owner.ChildObjects.IndexOf(doc), newTableRow);
                }
                else
                {
                    doc.ChildObjects.Add(newTableRow);
                }
            }
            else if (unit is CellUnit)
            {
                CellUnit cellUnit = unit as CellUnit;
                Spire.Doc.TableRow tableRow = doc as Spire.Doc.TableRow;
                //TableCell tableCell = new TableCell(doc.Document);

                TableCell newTableCell = (cellUnit.Source as TableCell).Clone() as TableCell;
                if (!unit.JustSource)
                {
                    newTableCell.ChildObjects.Clear();
                    foreach (var child in cellUnit.Children)
                    {
                        RenderToDocument(child, newTableCell);
                    }
                }

                if (isInsert)
                {
                    doc.Owner.ChildObjects.Insert(doc.Owner.ChildObjects.IndexOf(doc), newTableCell);
                }
                else
                {
                    doc.ChildObjects.Add(newTableCell);
                }
            }
            else if (unit is ParagraphUnit)
            {
                ParagraphUnit paragraphUnit = unit as ParagraphUnit;
                Paragraph newParagraph = null;

                if (unit.Source == null)
                {
                    newParagraph = new Paragraph(doc.Document);
                    foreach (var child in paragraphUnit.Children)
                    {
                        RenderToDocument(child, newParagraph);
                    }
                }
                else
                {
                    newParagraph = (paragraphUnit.Source as Paragraph).Clone() as Paragraph;
                    if (!unit.JustSource)
                    {
                        newParagraph.ChildObjects.Clear();
                        foreach (var child in paragraphUnit.Children)
                        {
                            RenderToDocument(child, newParagraph);
                        }
                    }
                }

                if (isInsert)
                {
                    doc.Owner.ChildObjects.Insert(doc.Owner.ChildObjects.IndexOf(doc), newParagraph);
                }
                else
                {
                    doc.ChildObjects.Add(newParagraph);
                }
            }
            else if (unit is TextRangeUnit)
            {
                TextRangeUnit textRangeUnit = unit as TextRangeUnit;
                Spire.Doc.Fields.TextRange newTextRange = null;

                if (unit.Source == null)
                {
                    newTextRange = new Spire.Doc.Fields.TextRange(doc.Document);
                    newTextRange.Text = textRangeUnit.Text;
                }
                else
                {
                    newTextRange = (textRangeUnit.Source as Spire.Doc.Fields.TextRange).Clone() as Spire.Doc.Fields.TextRange;
                    newTextRange.Text = textRangeUnit.Text;
                }

                doc.ChildObjects.Add(newTextRange);
            }
            else if (unit is PictureUnit)
            {
                PictureUnit pictureUnit = unit as PictureUnit;
                Paragraph paragraph = doc as Paragraph;
                //DocPicture docPicture = new DocPicture(doc.Document);
                DocPicture newPicture = null;
                if (unit.Source != null)
                {
                    newPicture = (pictureUnit.Source as DocPicture).Clone() as DocPicture;
                    if (!unit.JustSource)
                    {
                        newPicture.LoadImage(pictureUnit.Image);
                    }
                }
                else
                {
                    newPicture = new DocPicture(doc.Document);
                    newPicture.LoadImage(pictureUnit.Image);
                }

                if (pictureUnit.Width > 0)
                {
                    newPicture.Width = (float)pictureUnit.Width;
                }
                if (pictureUnit.Height > 0)
                {
                    newPicture.Height = (float)pictureUnit.Height;
                }

                paragraph.ChildObjects.Add(newPicture);
            }
        }
        #endregion

        #region Excel
        public void RenderToFile(WorkbookNode nodeBase, string outputName)
        {
            _rt.Hook.BeforeRender();
            RenderToExcelSource(nodeBase);
            _rt.Hook.AfterRender();
            var workbook = nodeBase.Source as Workbook;
            workbook.SaveToFile(outputName);
        }

        private void RenderToExcelSource(NodeBase nodeBase)
        {
            if (nodeBase.JustSource)
            {
                return;
            }

            if (nodeBase.FuncNode != null || nodeBase is TableNode || nodeBase is TextBoxNode)
            {
                List<Unit> units = nodeBase.RenderValue(null);
                foreach (var unit in units)
                {
                    RenderToWorkbook(unit, unit.Source, true);
                }
            }
            else
            {
                foreach (var child in nodeBase.Children)
                {
                    RenderToExcelSource(child);
                }
            }

            if (nodeBase is WorksheetNode)
            {
                var worksheetNode = nodeBase as WorksheetNode;
                foreach (var textbox in worksheetNode.TextBoxes)
                {
                    RenderToExcelSource(textbox);
                }
                foreach (var picture in worksheetNode.Pictures)
                {
                    RenderToExcelSource(picture);
                }
                RenderToExcelSource(worksheetNode.Table);
                //var units = worksheetNode.Table.RenderValue(null);
                //RenderToWorkbook(units[0], units[0].Source, true);
            }
        }

        private void RenderToWorkbook(Unit unit, object obj, bool isInsert = false)
        {
            if(unit is WorksheetUnit)
            {
                throw new NotImplementedException();
            }
            else if(unit is TableUnit)
            {
                TableUnit tableUnit = unit as TableUnit;

                Worksheet worksheet = obj as Worksheet;
                int count = worksheet.Rows.Count();
                // 在最后面添加两个空行（worksheet.Rows.Count()+1一次出现两个），然后每次都在倒数第一个空行前插入新的行
                worksheet.InsertRow(worksheet.Rows.Count() + 1);
                foreach (var child in tableUnit.Children)
                {
                    RenderToWorkbook(child, obj);
                }

                // 删掉原来的
                for (int i = 0; i <= count; i++)
                {
                    worksheet.DeleteRow(1);
                }
            }
            else if(unit is RowUnit)
            {
                RowUnit rowUnit = unit as RowUnit;

                CellRange sourceRow = rowUnit.Source as CellRange;
                CellRange newRow = sourceRow.Clone(sourceRow.Parent, null, sourceRow.Worksheet.Workbook);
                if (!unit.JustSource)
                {
                    //newRow.ClearAll();
                    //foreach (var cellUnit in rowUnit.Cells)
                    //{
                    //    RenderToWorkbook(cellUnit, newRow);
                    //}
                    for (int i = 0; i < rowUnit.Children.Count; i++)
                    {
                        RenderToWorkbook(rowUnit.Children[i], newRow.Cells[i]);
                    }
                }

                Worksheet worksheet = obj as Worksheet;
                worksheet.InsertRow(worksheet.Rows.Count());
                worksheet.Copy(newRow, worksheet, worksheet.Rows.Count()-1, 1, true, true);
                //Worksheet worksheet = obj as Worksheet;
                //worksheet.InsertRow(worksheet.Rows.Count());
                //CellRange row = worksheet.Rows.Last();
                //for (int i = 0; i < rowUnit.Children.Count; i++)
                //{
                //    RenderToWorkbook(rowUnit.Children[i], row.Cells[i]);
                //}
            }
            else if (unit is CellUnit)
            {
                CellUnit cellUnit = unit as CellUnit;

                string text = cellUnit.ToString();
                if (text.Length > 0)
                {
                    (obj as CellRange).Text = text;
                }
            }
            else if (unit is TextBoxUnit)
            {
                TextBoxUnit textboxUnit = unit as TextBoxUnit;
                
                string text = textboxUnit.ToString();
                if(text.Length > 0)
                {
                    (obj as ITextBox).Text = text;
                }
            }
            else if (unit is TextBoxUnit)
            {
                throw new NotImplementedException();
            }
            else if (unit is PictureUnit)
            {
                PictureUnit pictureUnit = unit as PictureUnit;
                ExcelPicture picture = obj as ExcelPicture;
                picture.Picture = pictureUnit.Image.Clone() as Image;

                if (pictureUnit.Width > 0)
                {
                    picture.Width = (int)pictureUnit.Width;
                }
                if (pictureUnit.Height > 0)
                {
                    picture.Height = (int)pictureUnit.Height;
                }
            }
        }
        #endregion

        #region PowerPoint
        public void RenderToFile(PresentationNode nodeBase, string outputName)
        {
            _rt.Hook.BeforeRender();
            RenderToPPTSource(nodeBase);
            _rt.Hook.AfterRender();
            var presentation = nodeBase.Source as Presentation;
            presentation.SaveToFile(outputName, Spire.Presentation.FileFormat.Pptx2013);
        }

        private void RenderToPPTSource(NodeBase nodeBase)
        {
            if (nodeBase.JustSource)
            {
                return;
            }

            if (nodeBase.FuncNode != null || nodeBase is ParagraphNode)
            {
                List<Unit> units = nodeBase.RenderValue(null);
                foreach (var unit in units)
                {
                    RenderToPresentation(unit, unit.Source, true);
                }
                if(nodeBase is ParagraphNode) // 这里暂时不会执行
                {
                    var paragraph = (nodeBase.Source as Spire.Presentation.TextParagraph);
                    ITextFrameProperties textFrame = null;
                    if (nodeBase.SourceParent is Spire.Presentation.IAutoShape)
                    {
                        textFrame = (nodeBase.SourceParent as Spire.Presentation.IAutoShape).TextFrame;
                    }
                    else if (nodeBase.SourceParent is Spire.Presentation.Cell)
                    {
                        textFrame = (nodeBase.SourceParent as Spire.Presentation.Cell).TextFrame;
                    }
                    int index = Helper.GetIndex(textFrame.Paragraphs.GetEnumerator(), paragraph);
                    textFrame.Paragraphs.RemoveAt(index);
                }
                else if(nodeBase is RowNode)
                {
                    var row = (nodeBase.Source as Spire.Presentation.TableRow);
                    var parent = nodeBase.SourceParent as Spire.Presentation.ITable;
                    int index = Helper.GetIndex(parent.TableRows.GetEnumerator(), row);
                    parent.TableRows.RemoveAt(index, true);
                }
            }
            else
            {
                foreach (var child in nodeBase.Children)
                {
                    RenderToPPTSource(child);
                }
            }
        }

        private void RenderToPresentation(Unit unit, object obj, bool isInsert = false)
        {
            if(unit is SlideUnit)
            {
                throw new NotImplementedException();
            }
            else if(unit is TextBoxUnit)
            {
                TextBoxUnit textBoxUnit = unit as TextBoxUnit;
                
                Spire.Presentation.IAutoShape textbox = unit.Source as Spire.Presentation.IAutoShape;
                textbox.TextFrame.Paragraphs.Clear();
                for (var i = 0; i < textBoxUnit.Children.Count; i++)
                {
                    RenderToPresentation(textBoxUnit.Children[i], textbox);
                }
            }
            else if(unit is TableUnit)
            {
                throw new NotImplementedException();
            }
            else if (unit is RowUnit)
            {
                RowUnit rowUnit = unit as RowUnit;
                Spire.Presentation.TableRow sourceRow = rowUnit.Source as Spire.Presentation.TableRow;
                ITable table = rowUnit.SourceParent as ITable;

                if (!isInsert)
                {
                    throw new NotImplementedException();
                }

                //Spire.Presentation.Cell[] arr = new Spire.Presentation.Cell[sourceRow.Count];
                //sourceRow.CopyTo(arr, 0);
                Spire.Presentation.TableRow newRow = sourceRow;


                if (!unit.JustSource)
                {
                    foreach (var child in rowUnit.Children)
                    {
                        RenderToPresentation(child, newRow);
                    }
                }

                int index = Helper.GetIndex(table.TableRows.GetEnumerator(), sourceRow);
                table.TableRows.Insert(index, sourceRow);
            }
            else if (unit is CellUnit)
            {
                CellUnit cellUnit = unit as CellUnit;

                if (isInsert)
                {
                    throw new NotImplementedException();
                }
                
                Spire.Presentation.TableRow row = obj as Spire.Presentation.TableRow;
                Spire.Presentation.Cell cell = unit.Source as Spire.Presentation.Cell;
                cell.TextFrame.Paragraphs.Clear();
                for(var i = 0; i < cellUnit.Children.Count; i++)
                {
                    RenderToPresentation(cellUnit.Children[i], cell);
                }
            }
            else if (unit is ParagraphUnit)
            {
                ParagraphUnit paragraphUnit = unit as ParagraphUnit;
                Spire.Presentation.TextParagraph paragraph = paragraphUnit.Source as Spire.Presentation.TextParagraph;

                Spire.Presentation.TextParagraph newParagraph = null;
                if (unit.Source == null)
                {
                    newParagraph = new Spire.Presentation.TextParagraph();
                    foreach (var child in paragraphUnit.Children)
                    {
                        if(child is PictureUnit)
                        {
                            RenderToPresentation(child, unit.SourceParent);
                        }
                        else
                        {
                            RenderToPresentation(child, newParagraph);
                        }
                    }
                }
                else
                {
                    // 此处应该复制段落
                    newParagraph = new Spire.Presentation.TextParagraph();

                    if (!unit.JustSource)
                    {
                        newParagraph.TextRanges.Clear();
                        foreach (var child in paragraphUnit.Children)
                        {
                            if (child is PictureUnit)
                            {
                                RenderToPresentation(child, unit.SourceParent);
                            }
                            else
                            {
                                RenderToPresentation(child, newParagraph);
                            }
                        }
                    }
                }

                ITextFrameProperties textFrame = null;
                if(unit.SourceParent is Spire.Presentation.IAutoShape)
                {
                    textFrame = (unit.SourceParent as Spire.Presentation.IAutoShape).TextFrame;
                }
                else if(unit.SourceParent is Spire.Presentation.Cell)
                {
                    textFrame = (unit.SourceParent as Spire.Presentation.Cell).TextFrame;
                }

                if (isInsert)
                {
                    Spire.Presentation.TextParagraph sourceParagraph = obj as Spire.Presentation.TextParagraph;
                    int index = Helper.GetIndex(textFrame.Paragraphs.GetEnumerator(), sourceParagraph);
                    textFrame.Paragraphs.Insert(index, newParagraph);
                }
                else
                {
                    textFrame.Paragraphs.Append(newParagraph);
                }
            }
            else if (unit is TextRangeUnit)
            {
                TextRangeUnit textRangeUnit = unit as TextRangeUnit;
                Spire.Presentation.TextRange newTextRange = null;

                if (unit.Source != null && unit.Source is Spire.Presentation.TextRange)
                {
                    var sourceTextRange = textRangeUnit.Source as Spire.Presentation.TextRange;
                    newTextRange = new Spire.Presentation.TextRange(sourceTextRange);
                    newTextRange.Text = textRangeUnit.Text;
                }
                else
                {
                    newTextRange = new Spire.Presentation.TextRange();
                    newTextRange.Text = textRangeUnit.Text;
                }

                Spire.Presentation.TextParagraph paragraph = obj as Spire.Presentation.TextParagraph;
                paragraph.TextRanges.Append(newTextRange);
            }
            else if (unit is PictureUnit)
            {
                PictureUnit pictureUnit = unit as PictureUnit;

                if(pictureUnit.Source is Spire.Presentation.SlidePicture)
                {
                    Spire.Presentation.SlidePicture picture = pictureUnit.Source as Spire.Presentation.SlidePicture;
                    if (!unit.JustSource)
                    {
                        Spire.Presentation.Drawing.IImageData image = picture.Slide.Presentation.Images.Append(pictureUnit.Image);
                        picture.PictureFill.Picture.EmbedImage = image;
                    }

                    if (pictureUnit.Width > 0)
                    {
                        picture.Width = (float)pictureUnit.Width;
                    }
                    if (pictureUnit.Height > 0)
                    {
                        picture.Height = (float)pictureUnit.Height;
                    }
                }
                else
                {
                    // 说明在单元格中或文本框中，作为背景存在即可
                    Spire.Presentation.Drawing.FillFormat fillFormat = null;
                    Presentation presentation = null;
                    if(obj is Spire.Presentation.IAutoShape)
                    {
                        fillFormat = (obj as IAutoShape).Fill;
                        presentation = (obj as IAutoShape).Slide.Presentation;
                    }
                    else if(obj is Spire.Presentation.Cell)
                    {
                        fillFormat = (obj as Spire.Presentation.Cell).FillFormat;
                        presentation = (obj as Spire.Presentation.Cell).Slide.Presentation;
                    }
                    fillFormat.FillType = Spire.Presentation.Drawing.FillFormatType.Picture;
                    Spire.Presentation.Drawing.IImageData image = presentation.Images.Append(pictureUnit.Image);
                    fillFormat.PictureFill.Picture.EmbedImage = image;
                    fillFormat.PictureFill.FillType = Spire.Presentation.Drawing.PictureFillType.Stretch;
                }
            }
        }
        #endregion
    }
    public interface IRender
    {
        void RenderToFile(DocumentNode nodeBase, string outputName);
        void RenderToFile(WorkbookNode nodeBase, string outputName);
        void RenderToFile(PresentationNode nodeBase, string outputName);
    }
    class Helper
    {
        public static int GetIndex(IEnumerator arr, object item)
        {
            int index = -1;
            int i = 0;
            while(arr.MoveNext())
            {
                if(arr.Current == item)
                {
                    index = i;
                    break;
                }
                i++;
            }
            return index;
        }
    }
    #endregion

    #region 运行时，包括钩子，运行消息等
    public class EngineRuntime
    {
        public IHook Hook { get; set; }

        public EngineRuntime()
        {
            Hook = new Hook();
        }
    }

    public class Hook : IHook
    {
        public void AfterData()
        {

        }

        public void AfterMatch()
        {

        }

        public void AfterParse()
        {

        }

        public void AfterRender()
        {

        }

        public void AfterTask()
        {

        }

        public void AfterTrans()
        {

        }

        public void BeforeMatch()
        {

        }

        public void BeforeParse()
        {

        }

        public void BeforeRender()
        {

        }

        public void BeforeTask()
        {

        }

        public void BeforeTrans()
        {

        }
    }
    public interface IHook
    {
        void BeforeTask();
        void BeforeParse();
        void AfterParse();
        void BeforeTrans();
        void BeforeMatch();
        void AfterMatch();
        void AfterData();
        void BeforeRender();
        void AfterRender();
        void AfterTrans();
        void AfterTask();
    }
    #endregion
}
